﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Collections.ObjectModel;

namespace Summerset.Isbndb.Books {

    /// <summary>
    /// Represents information about an individual publication returned from the books collection.
    /// </summary>
    public class BookElement : ResponseElementBase
    {
        #region Constructor + Overridden Properties

        /// <summary>
        /// Initializes a new instance of the <see cref="BookElement"/> class.
        /// </summary>
        /// <remarks>
        /// Calling this constructor requires that a BookData XmlNode has already been retrieved by the <see cref="BooksResponsePage"/> 
        /// that this object belongs to.  
        /// Only a single BookData element may be provided to this constructor.
        /// </remarks>
        /// <param name="element">The XML element that will be used as the underlying data source for this object</param>
        public BookElement(XmlElement element) : base (element) { }

        /// <summary>
        /// Gets the element name that is expected by the constructor of this object.
        /// If the provided element name does not match the expected element name, an exception will be thrown.
        /// </summary>
        protected override string ExpectedElementName
        {
            get { return "BookData"; }
        }

        #endregion
        #region Common Response Properties

        /*
         * The properties to each of these elements and attributes 
         * are always included in the <BookData> elements returned from
         * the BooksCollection endpoint, so all of these properties just make 
         * the assumption that a single node will be found matching the
         * xPath query given.
         */

        /// <summary>
        /// Gets a string that uniquely identifies this publication within the ISBNdb API.
        /// </summary>
        public string ID {
            get {
                return _element.Attributes["book_id"].Value;
            }
        }

        /// <summary>
        /// Gets the 10-digit ISBN number for this publication.
        /// </summary>
        public string ISBN {
            get {
                return _element.Attributes["isbn"].Value;
            }
        }

        /// <summary>
        /// Gets the 13-digit ISBN representation for this publication.
        /// </summary>
        public string ISBN13 {
            get {
                return _element.Attributes["isbn13"].Value;
            }
        }

        /// <summary>
        /// Gets the title of this publication.
        /// </summary>
        public string Title {
            get {

                // Title is a node that should always be present in books response.
                return _element.SelectSingleNode("/Title").Value;
            }
        }

        /// <summary>
        /// Gets a string that represents the title, including any subtitles of this publication.
        /// </summary>
        public string TitleLong {
            get {
                // Title long should also always be included in the response
                return _element.SelectSingleNode("/TitleLong").Value;
            }
        }

        /// <summary>
        /// Gets a free-form string that identifies the authors of this publication.
        /// </summary>
        public string AuthorsText {
            get {
                return _element.SelectSingleNode("/AuthorsText").Value;
            }
        }

        /// <summary>
        /// Gets a free-form string that identifies the publisher.
        /// </summary>
        public string PublisherText {
            get {
                return _element.SelectSingleNode("/PublisherText ").Value;
            }
        }

        /// <summary>
        /// Gets a string that uniquely identifies this publisher to the ISBNDB.com API.
        /// </summary>
        public string PublisherID {
            get {
                return _element.SelectSingleNode("/PublisherText ").Attributes["publisher_id"].Value;
            }
        }

        #endregion
        #region Additional Response Properties

        /// <summary>
        /// OPTIONAL RESULT: Gets additional information about this publication.  
        /// This property must be explicitly requested by providing the 'details' constant to the BooksRequest constructor.
        /// </summary>        
        public BookElementDetail Detail
        {
            get
            {
                if (_detail == null)
                {
                    var bookData = _element.SelectSingleNode("/Details");

                    if (bookData != null)
                    {
                        _detail = new BookElementDetail(bookData);
                    }
                }


                return _detail;
            }
        }
        BookElementDetail _detail;

        /// <summary>
        /// OPTIONAL RESULT: Gets information about the authors associated with this publication.
        /// This property must be explicitly requested by providing the 'authors' constant to the BooksRequest constructor.
        /// </summary>
        public ReadOnlyCollection<BookElementAuthor> Authors
        {
            get
            {
                if (_authors == null)
                {
                    var authorsNodes = _element.SelectNodes("/Authors/Person");
                    
                    if (authorsNodes != null)
                    {
                    
                        Collection<BookElementAuthor> collection = new Collection<BookElementAuthor>();

                        foreach (var authorNode in authorsNodes)
                            collection.Add(new BookElementAuthor((XmlNode)authorNode));

                        _authors = new ReadOnlyCollection<BookElementAuthor>(collection);
                    }
                }

                return _authors;
            }
        }
        ReadOnlyCollection<BookElementAuthor> _authors;
        
        /// <summary>
        /// OPTIONAL RESULT: Gets the MAchine Readable Cataloging information for this publication.
        /// This must be requested explicitly by providing the 'marc' constant to the BooksRequest constructor.
        /// </summary>
        public ReadOnlyCollection<BookElementMarc> Marcs
        {
            get
            {
                if (_marcs == null)
                {
                    var marcNodes = _element.SelectNodes("/MARCRecords/MARC");

                    if (marcNodes != null)
                    {
                        Collection<BookElementMarc> collection = new Collection<BookElementMarc>();

                        foreach (var marcNode in marcNodes)
                            collection.Add(new BookElementMarc((XmlNode)marcNode));

                        _marcs = new ReadOnlyCollection<BookElementMarc>(collection);
                    }
                }

                return _marcs;
            }
        }
        ReadOnlyCollection<BookElementMarc> _marcs;

        /// <summary>
        /// OPTIONAL RESULT: Gets the current and/or historical pricing information for this publication.
        /// This must be requested explicitly by providing the 'prices' or 'pricehistory' constant to the BooksRequest constructor.
        /// </summary>
        public ReadOnlyCollection<BookElementPrice> Prices
        {
            get
            {
                if (_prices == null)
                {
                    var priceNodes = _element.SelectNodes("/Prices/Price");

                    if (priceNodes != null)
                    {
                        Collection<BookElementPrice> collection = new Collection<BookElementPrice>();

                        foreach (var priceNode in priceNodes)
                            collection.Add(new BookElementPrice((XmlNode)priceNode));

                        _prices = new ReadOnlyCollection<BookElementPrice>(collection);
                    }
                }

                return _prices;
            }
        }
        ReadOnlyCollection<BookElementPrice> _prices;

        /// <summary>
        /// OPTIONAL RESULT: Gets the subjects that this publication is listed in.
        /// This must be requested explicitly by providing the 'subjects' constant to the BooksRequest constructor.
        /// </summary>
        public ReadOnlyCollection<BookElementSubject> Subjects
        {
            get
            {
                if (_subjects == null)
                {
                    var subjectNodes = _element.SelectNodes("/Subjects/Subject");

                    if (subjectNodes != null)
                    {
                        Collection<BookElementSubject> collection = new Collection<BookElementSubject>();

                        foreach (var subjectNode in subjectNodes)
                            collection.Add(new BookElementSubject((XmlNode)subjectNode));

                        _subjects = new ReadOnlyCollection<BookElementSubject>(collection);
                    }
                }

                return _subjects;
            }
        }
        ReadOnlyCollection<BookElementSubject> _subjects;

        #endregion
    }
}
